package cn.iocoder.yudao.module.system.service.transaction;

import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.system.controller.admin.examscore.vo.ExamScoreImportRespVO;
import cn.iocoder.yudao.module.system.controller.admin.examscore.vo.NewExamScoreImportExcelVO;
import cn.iocoder.yudao.module.system.dal.dataobject.examclass.ExamClassDO;
import cn.iocoder.yudao.module.system.dal.dataobject.examclass.ExamClassStudentDO;
import cn.iocoder.yudao.module.system.dal.dataobject.examscore.ExamScoreDO;
import cn.iocoder.yudao.module.system.dal.mysql.examclass.ExamClassMapper;
import cn.iocoder.yudao.module.system.dal.mysql.examclass.ExamClassStudentMapper;
import cn.iocoder.yudao.module.system.dal.mysql.examscore.ExamScoreMapper;
import cn.iocoder.yudao.module.system.dal.mysql.examscore.ExamScoreMapperBatch;
import cn.iocoder.yudao.module.system.service.cache.ScoreImportCacheService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 事务优化服务
 * 通过优化事务管理策略，减少长事务对系统性能的影响
 *
 * @author 芋道源码
 */
@Service
@Slf4j
public class TransactionOptimizedService {

    @Resource
    private ExamClassMapper examClassMapper;
    
    @Resource
    private ExamClassStudentMapper examClassStudentMapper;
    
    @Resource
    private ExamScoreMapper examScoreMapper;
    
    @Resource
    private ExamScoreMapperBatch examScoreMapperBatch;
    
    @Resource
    private ScoreImportCacheService cacheService;
    
    @Resource
    private TransactionTemplate transactionTemplate;
    
    // 小批次大小，减少单个事务的数据量
    private static final int SMALL_BATCH_SIZE = 100;
    private static final int MEDIUM_BATCH_SIZE = 200;
    private static final int LARGE_BATCH_SIZE = 500;
    
    /**
     * 优化版成绩导入 - 使用短事务策略
     * 将大事务拆分为多个小事务，减少锁定时间
     */
    public ExamScoreImportRespVO importScoresWithOptimizedTransaction(List<NewExamScoreImportExcelVO> importDataList) {
        log.info("开始优化事务版成绩导入，数据量: {}", importDataList.size());
        
        ExamScoreImportRespVO finalResult = ExamScoreImportRespVO.builder()
                .createUsernames(new ArrayList<>())
                .updateUsernames(new ArrayList<>())
                .duplicateUsernames(new ArrayList<>())
                .failureUsernames(new java.util.HashMap<>())
                .build();
        
        // 1. 预处理阶段 - 无事务
        BatchProcessingPlan plan = createProcessingPlan(importDataList);
        log.info("处理计划创建完成，需要创建{}个班级，{}个学生，{}条成绩", 
                plan.getClassesToCreate().size(), plan.getStudentsToCreate().size(), plan.getScoresToProcess().size());
        
        // 2. 分阶段执行 - 每个阶段使用独立的短事务
        try {
            // 阶段1：创建班级（短事务）
            if (!plan.getClassesToCreate().isEmpty()) {
                createClassesInBatches(plan.getClassesToCreate());
                log.info("班级创建完成");
            }
            
            // 阶段2：创建学生（短事务）
            if (!plan.getStudentsToCreate().isEmpty()) {
                createStudentsInBatches(plan.getStudentsToCreate());
                log.info("学生创建完成");
            }
            
            // 阶段3：处理成绩数据（分批短事务）
            ExamScoreImportRespVO scoreResult = processScoresInBatches(plan.getScoresToProcess());
            mergeResults(finalResult, scoreResult);
            log.info("成绩处理完成");
            
        } catch (Exception e) {
            log.error("优化事务版成绩导入失败", e);
            throw new RuntimeException("成绩导入失败: " + e.getMessage(), e);
        }
        
        return finalResult;
    }
    
    /**
     * 创建处理计划
     * 分析数据，制定最优的处理策略
     */
    private BatchProcessingPlan createProcessingPlan(List<NewExamScoreImportExcelVO> importDataList) {
        BatchProcessingPlan plan = new BatchProcessingPlan();
        
        // 分析需要创建的班级
        List<String> classNames = importDataList.stream()
                .map(NewExamScoreImportExcelVO::getClassName)
                .distinct()
                .collect(java.util.stream.Collectors.toList());
        
        // 检查哪些班级需要创建
        for (String className : classNames) {
            ExamClassDO existingClass = cacheService.getClassByName(className);
            if (existingClass == null) {
                plan.getClassesToCreate().add(className);
            }
        }
        
        // 分析需要创建的学生
        for (NewExamScoreImportExcelVO data : importDataList) {
            ExamClassStudentDO existingStudent = cacheService.getStudentByNameAndClass(
                    data.getStudentName(), data.getClassName());
            if (existingStudent == null) {
                plan.getStudentsToCreate().add(data);
            }
        }
        
        // 所有数据都需要处理成绩
        plan.setScoresToProcess(importDataList);
        
        return plan;
    }
    
    /**
     * 分批创建班级 - 使用小事务
     */
    private void createClassesInBatches(List<String> classNames) {
        int totalSize = classNames.size();
        
        for (int i = 0; i < totalSize; i += SMALL_BATCH_SIZE) {
            int endIndex = Math.min(i + SMALL_BATCH_SIZE, totalSize);
            List<String> batch = classNames.subList(i, endIndex);
            
            // 使用独立的短事务
            transactionTemplate.execute(status -> {
                try {
                    List<ExamClassDO> classesToInsert = new ArrayList<>();
                    
                    for (String className : batch) {
                        ExamClassDO clazz = createClassEntity(className);
                        classesToInsert.add(clazz);
                    }
                    
                    if (!classesToInsert.isEmpty()) {
                        examClassMapper.insertBatch(classesToInsert);
                        
                        // 更新缓存
                        for (ExamClassDO clazz : classesToInsert) {
                            cacheService.cacheClass(clazz);
                        }
                    }
                    
                    log.debug("批量创建班级完成，批次大小: {}", batch.size());
                    return null;
                    
                } catch (Exception e) {
                    log.error("创建班级批次失败", e);
                    status.setRollbackOnly();
                    throw e;
                }
            });
        }
    }
    
    /**
     * 分批创建学生 - 使用小事务
     */
    private void createStudentsInBatches(List<NewExamScoreImportExcelVO> studentsData) {
        int totalSize = studentsData.size();
        
        for (int i = 0; i < totalSize; i += SMALL_BATCH_SIZE) {
            int endIndex = Math.min(i + SMALL_BATCH_SIZE, totalSize);
            List<NewExamScoreImportExcelVO> batch = studentsData.subList(i, endIndex);
            
            // 使用独立的短事务
            transactionTemplate.execute(status -> {
                try {
                    List<ExamClassStudentDO> studentsToInsert = new ArrayList<>();
                    
                    for (NewExamScoreImportExcelVO data : batch) {
                        ExamClassDO clazz = cacheService.getClassByName(data.getClassName());
                        if (clazz != null) {
                            ExamClassStudentDO student = createStudentEntity(data, clazz.getId());
                            studentsToInsert.add(student);
                        }
                    }
                    
                    if (!studentsToInsert.isEmpty()) {
                        examClassStudentMapper.insertBatch(studentsToInsert);
                        
                        // 更新缓存
                        for (ExamClassStudentDO student : studentsToInsert) {
                            cacheService.cacheStudent(student);
                        }
                    }
                    
                    log.debug("批量创建学生完成，批次大小: {}", batch.size());
                    return null;
                    
                } catch (Exception e) {
                    log.error("创建学生批次失败", e);
                    status.setRollbackOnly();
                    throw e;
                }
            });
        }
    }
    
    /**
     * 分批处理成绩 - 使用中等大小事务
     */
    private ExamScoreImportRespVO processScoresInBatches(List<NewExamScoreImportExcelVO> scoresData) {
        ExamScoreImportRespVO result = ExamScoreImportRespVO.builder()
                .createUsernames(new ArrayList<>())
                .updateUsernames(new ArrayList<>())
                .duplicateUsernames(new ArrayList<>())
                .failureUsernames(new java.util.HashMap<>())
                .build();
        
        int totalSize = scoresData.size();
        AtomicInteger processedCount = new AtomicInteger(0);
        
        for (int i = 0; i < totalSize; i += MEDIUM_BATCH_SIZE) {
            final int startIndex = i;
            final int endIndex = Math.min(i + MEDIUM_BATCH_SIZE, totalSize);
            List<NewExamScoreImportExcelVO> batch = scoresData.subList(i, endIndex);
            
            // 使用独立的中等大小事务
            ExamScoreImportRespVO batchResult = transactionTemplate.execute(status -> {
                try {
                    return processSingleBatchScores(batch);
                } catch (Exception e) {
                    log.error("处理成绩批次失败，批次范围: {}-{}", startIndex, endIndex, e);
                    status.setRollbackOnly();
                    
                    // 返回失败结果
                    ExamScoreImportRespVO failResult = ExamScoreImportRespVO.builder()
                            .createUsernames(new ArrayList<>())
                            .updateUsernames(new ArrayList<>())
                            .duplicateUsernames(new ArrayList<>())
                            .failureUsernames(new java.util.HashMap<>())
                            .build();
                    
                    for (NewExamScoreImportExcelVO data : batch) {
                        failResult.getFailureUsernames().put(data.getStudentName(), 
                                "批次处理失败: " + e.getMessage());
                    }
                    
                    return failResult;
                }
            });
            
            if (batchResult != null) {
                mergeResults(result, batchResult);
                processedCount.addAndGet(batch.size());
                
                // 记录进度
                if (processedCount.get() % 1000 == 0 || processedCount.get() == totalSize) {
                    log.info("成绩处理进度: {}/{}", processedCount.get(), totalSize);
                }
            }
        }
        
        return result;
    }
    
    /**
     * 处理单个批次的成绩数据
     * 在事务内执行，保证数据一致性
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public ExamScoreImportRespVO processSingleBatchScores(List<NewExamScoreImportExcelVO> batch) {
        ExamScoreImportRespVO result = ExamScoreImportRespVO.builder()
                .createUsernames(new ArrayList<>())
                .updateUsernames(new ArrayList<>())
                .duplicateUsernames(new ArrayList<>())
                .failureUsernames(new java.util.HashMap<>())
                .build();
        
        List<ExamScoreDO> scoresToInsert = new ArrayList<>();
        List<ExamScoreDO> scoresToUpdate = new ArrayList<>();
        
        for (NewExamScoreImportExcelVO data : batch) {
            try {
                // 获取学生信息
                ExamClassStudentDO student = cacheService.getStudentByNameAndClass(
                        data.getStudentName(), data.getClassName());
                
                if (student == null) {
                    result.getFailureUsernames().put(data.getStudentName(), "学生信息不存在");
                    continue;
                }
                
                // 获取班级信息
                ExamClassDO clazz = cacheService.getClassByName(data.getClassName());
                if (clazz == null) {
                    result.getFailureUsernames().put(data.getStudentName(), "班级信息不存在");
                    continue;
                }
                
                // 检查是否已存在成绩记录
                Long examId = 1L; // 这里应该从上下文获取
                ExamScoreDO existingScore = cacheService.getScoreByExamAndStudent(examId, student.getStudentId());
                
                // 创建成绩记录
                ExamScoreDO scoreRecord = createScoreRecord(data, examId, student, clazz);
                
                if (existingScore != null) {
                    // 更新现有记录
                    scoreRecord.setId(existingScore.getId());
                    scoresToUpdate.add(scoreRecord);
                    result.getUpdateUsernames().add(data.getStudentName());
                } else {
                    // 新增记录
                    scoresToInsert.add(scoreRecord);
                    result.getCreateUsernames().add(data.getStudentName());
                }
                
            } catch (Exception e) {
                log.error("处理学生{}数据失败", data.getStudentName(), e);
                result.getFailureUsernames().put(data.getStudentName(), e.getMessage());
            }
        }
        
        // 批量执行数据库操作
        if (!scoresToInsert.isEmpty()) {
            examScoreMapperBatch.insertBatchScores(scoresToInsert);
            
            // 更新缓存
            for (ExamScoreDO score : scoresToInsert) {
                cacheService.cacheScore(score);
            }
        }
        
        if (!scoresToUpdate.isEmpty()) {
            examScoreMapperBatch.updateBatchScores(scoresToUpdate);
            
            // 清除相关缓存
            for (ExamScoreDO score : scoresToUpdate) {
                cacheService.evictScoreCache(score.getExamId(), score.getStudentId());
            }
        }
        
        return result;
    }
    
    /**
     * 只读事务查询 - 用于数据验证和统计
     */
    @Transactional(readOnly = true)
    public ValidationResult validateImportData(List<NewExamScoreImportExcelVO> importDataList) {
        ValidationResult result = new ValidationResult();
        
        // 验证班级信息
        List<String> invalidClasses = new ArrayList<>();
        for (NewExamScoreImportExcelVO data : importDataList) {
            if (data.getClassName() == null || data.getClassName().trim().isEmpty()) {
                invalidClasses.add("空班级名称");
            }
        }
        result.setInvalidClasses(invalidClasses);
        
        // 验证学生信息
        List<String> invalidStudents = new ArrayList<>();
        for (NewExamScoreImportExcelVO data : importDataList) {
            if (data.getStudentName() == null || data.getStudentName().trim().isEmpty()) {
                invalidStudents.add("空学生姓名");
            }
        }
        result.setInvalidStudents(invalidStudents);
        
        // 验证成绩信息
        List<String> invalidScores = new ArrayList<>();
        for (NewExamScoreImportExcelVO data : importDataList) {
            if (data.getTotalOriginalScore() == null || data.getTotalOriginalScore().doubleValue() < 0) {
                invalidScores.add(data.getStudentName() + ": 无效总分");
            }
        }
        result.setInvalidScores(invalidScores);
        
        result.setValid(invalidClasses.isEmpty() && invalidStudents.isEmpty() && invalidScores.isEmpty());
        
        return result;
    }
    
    // ==================== 辅助方法 ====================
    
    private ExamClassDO createClassEntity(String className) {
        ExamClassDO clazz = new ExamClassDO();
        clazz.setClassName(className);
        
        // 设置当前登录用户的部门ID（用于数据权限控制）
        Long currentUserDeptId = SecurityFrameworkUtils.getLoginUserDeptId();
        if (currentUserDeptId != null) {
            clazz.setDeptId(currentUserDeptId);
            log.debug("设置班级部门ID: {}, 班级: {}", currentUserDeptId, className);
        } else {
            log.warn("当前登录用户未设置部门ID，班级将不设置部门信息: {}", className);
        }
        
        // 设置其他必要字段
        return clazz;
    }
    
    private ExamClassStudentDO createStudentEntity(NewExamScoreImportExcelVO data, Long classId) {
        ExamClassStudentDO student = new ExamClassStudentDO();
        student.setStudentName(data.getStudentName());
        student.setClassName(data.getClassName());
        student.setClassId(classId);
        // 设置其他必要字段
        return student;
    }
    
    private ExamScoreDO createScoreRecord(NewExamScoreImportExcelVO data, Long examId, 
                                         ExamClassStudentDO student, ExamClassDO clazz) {
        ExamScoreDO score = new ExamScoreDO();
        score.setExamId(examId);
        score.setStudentId(student.getStudentId());
        score.setClassId(clazz.getId());
        score.setTotalScore(data.getTotalOriginalScore());
        // 设置其他字段
        return score;
    }
    
    private void mergeResults(ExamScoreImportRespVO target, ExamScoreImportRespVO source) {
        target.getCreateUsernames().addAll(source.getCreateUsernames());
        target.getUpdateUsernames().addAll(source.getUpdateUsernames());
        target.getFailureUsernames().putAll(source.getFailureUsernames());
    }
    
    // ==================== 内部类 ====================
    
    /**
     * 批处理计划
     */
    public static class BatchProcessingPlan {
        private List<String> classesToCreate = new ArrayList<>();
        private List<NewExamScoreImportExcelVO> studentsToCreate = new ArrayList<>();
        private List<NewExamScoreImportExcelVO> scoresToProcess = new ArrayList<>();
        
        // Getters and Setters
        public List<String> getClassesToCreate() { return classesToCreate; }
        public void setClassesToCreate(List<String> classesToCreate) { this.classesToCreate = classesToCreate; }
        
        public List<NewExamScoreImportExcelVO> getStudentsToCreate() { return studentsToCreate; }
        public void setStudentsToCreate(List<NewExamScoreImportExcelVO> studentsToCreate) { this.studentsToCreate = studentsToCreate; }
        
        public List<NewExamScoreImportExcelVO> getScoresToProcess() { return scoresToProcess; }
        public void setScoresToProcess(List<NewExamScoreImportExcelVO> scoresToProcess) { this.scoresToProcess = scoresToProcess; }
    }
    
    /**
     * 验证结果
     */
    public static class ValidationResult {
        private boolean valid;
        private List<String> invalidClasses = new ArrayList<>();
        private List<String> invalidStudents = new ArrayList<>();
        private List<String> invalidScores = new ArrayList<>();
        
        // Getters and Setters
        public boolean isValid() { return valid; }
        public void setValid(boolean valid) { this.valid = valid; }
        
        public List<String> getInvalidClasses() { return invalidClasses; }
        public void setInvalidClasses(List<String> invalidClasses) { this.invalidClasses = invalidClasses; }
        
        public List<String> getInvalidStudents() { return invalidStudents; }
        public void setInvalidStudents(List<String> invalidStudents) { this.invalidStudents = invalidStudents; }
        
        public List<String> getInvalidScores() { return invalidScores; }
        public void setInvalidScores(List<String> invalidScores) { this.invalidScores = invalidScores; }
    }
}